package com.hjj.homieMatching.controller;

import com.hjj.homieMatching.common.BaseResponse;
import com.hjj.homieMatching.common.ErrorCode;
import com.hjj.homieMatching.common.ResultUtils;
import com.hjj.homieMatching.constant.RedisConstant;
import com.hjj.homieMatching.exception.BusinessException;
import com.hjj.homieMatching.manager.MinIOManager;
import com.hjj.homieMatching.manager.RedisLimiterManager;
import com.hjj.homieMatching.model.domain.User;
import com.hjj.homieMatching.service.UserService;
import com.hjj.homieMatching.utils.MinIOConnectionTester;
import io.minio.MinioClient;
import io.minio.StatObjectArgs;
import io.minio.StatObjectResponse;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/image")
public class ImageController {

    private static final Logger log = LoggerFactory.getLogger(ImageController.class);

    @Resource
    private MinIOManager minIOManager;

    @Resource
    private RedisLimiterManager redisLimiterManager;

    @Resource
    private UserService userService;

    private String handleFileUpload(MultipartFile file, String rateLimitKey, int permits, int rate) {
        // 限流检查
        String userId = rateLimitKey.split(":")[1];
        try {
            redisLimiterManager.doRateLimiter(rateLimitKey, permits, rate);
        } catch (BusinessException e) {
            String message = String.format("上传频率超限 (每%d秒%d次)", rate, permits);
            throw new BusinessException(ErrorCode.TOO_MANY_REQUEST, message);
        }
        
        // 记录诊断信息以辅助排查问题
        String diagnostics = String.format(
                "客户端ID: %s, 令牌: %d/%d秒, MinIO端点: %s",
                userId, permits, rate, minIOManager.getEndpoint()
        );
        log.debug("开始上传文件, {}", diagnostics);
        
        try {
            return minIOManager.upload(file);
        } catch (Exception e) {
            // 记录详细的错误信息
            log.error("MinIO上传失败: {}, 诊断信息: {}", e.getMessage(), diagnostics, e);
            
            // 将底层异常包装为业务异常，保持一致的错误处理
            String errorMessage = "文件上传服务暂时不可用";
            if (e.getMessage() != null && !e.getMessage().isEmpty()) {
                // 安全地将部分错误消息传递给前端，但不暴露敏感信息
                if (e.getMessage().contains("连接被拒绝") || e.getMessage().contains("连接超时")) {
                    errorMessage = "文件存储服务连接失败，请联系管理员";
                } else if (e.getMessage().contains("认证失败")) {
                    errorMessage = "文件存储服务认证失败，请联系管理员";
                } else if (e.getMessage().contains("无法创建")) {
                    errorMessage = "无法创建文件存储目录，请联系管理员";
                }
            }
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, errorMessage);
        }
    }

    @PostMapping("/blog/coverImage/upload")
    public BaseResponse<String> uploadBlogCoverImage(
            @RequestPart MultipartFile file,
            HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        String url = handleFileUpload(
                file,
                RedisConstant.BLOG_COVER_IMAGE_UPLOAD_KEY + loginUser.getId(),
                1,  // 每分钟1次
                60   // 时间窗口60秒
        );
        return ResultUtils.success(url);
    }

    @PostMapping("/blog/image/upload")
    public BaseResponse<String> uploadBlogImage(
            @RequestPart MultipartFile file,
            HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        String url = handleFileUpload(
                file,
                RedisConstant.BLOG_IMAGE_UPLOAD_KEY + loginUser.getId(),
                2,  // 每分钟2次
                60 // 时间窗口60秒
        );
        return ResultUtils.success(url);
    }

    @PostMapping("/avatar/upload")
    public BaseResponse<String> uploadAvatar(
            @RequestPart(required = false) MultipartFile file,
            HttpServletRequest request) {
        User loginUser = userService.getLoginUser(request);
        String url;
        
        // 如果没有上传文件，则使用默认头像
        if (file == null || file.isEmpty()) {
            url = "https://img.yzcdn.cn/vant/cat.jpeg";
        } else {
            // 处理文件上传
            url = handleFileUpload(
                    file,
                    RedisConstant.AVATAR_UPLOAD_KEY + loginUser.getId(),
                    100,  // 每分钟100次 (大幅提高，原来是30次)
                    60  // 时间窗口60秒
            );
        }
        
        // 更新用户头像 URL
        boolean updateResult = userService.updateAvatar(loginUser.getId(), url);
        if (!updateResult) {
            // 可以选择抛出异常或记录日志，这里简单返回错误信息
            throw new BusinessException(ErrorCode.SYSTEM_ERROR, "更新头像失败");
        }
        return ResultUtils.success(url);
    }

    /**
     * 获取文件上传服务配置信息（仅供调试）
     */
    @PostMapping("/config/info")
    public BaseResponse<Map<String, String>> getUploadConfigInfo(HttpServletRequest request) {
        // 验证当前用户是否为管理员
        if (!userService.isAdmin(request)) {
            throw new BusinessException(ErrorCode.NO_AUTH, "仅管理员可查看配置信息");
        }
        
        Map<String, String> configInfo = new HashMap<>();
        configInfo.put("endpoint", minIOManager.getEndpoint());
        configInfo.put("bucketName", minIOManager.getBucketName());
        configInfo.put("folderName", minIOManager.getFolderName());
        
        // 不返回敏感凭证
        configInfo.put("accessKeyConfigured", 
                minIOManager.getAccessKey() != null && !minIOManager.getAccessKey().isEmpty() ? "是" : "否");
        configInfo.put("secretKeyConfigured", 
                minIOManager.getSecretKey() != null && !minIOManager.getSecretKey().isEmpty() ? "是" : "否");
        
        // 添加服务器信息
        configInfo.put("server_temp_dir", System.getProperty("java.io.tmpdir"));
        configInfo.put("server_time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        
        // 测试连接
        try {
            minIOManager.testConnection();
            configInfo.put("connection_status", "正常");
        } catch (Exception e) {
            configInfo.put("connection_status", "异常: " + e.getMessage());
        }
        
        return ResultUtils.success(configInfo);
    }

    /**
     * 测试MinIO连接
     */
    @PostMapping("/minio/test")
    public BaseResponse<Map<String, Object>> testMinIOConnection(HttpServletRequest request) {
        // 验证当前用户是否为管理员
        if (!userService.isAdmin(request)) {
            throw new BusinessException(ErrorCode.NO_AUTH, "仅管理员可执行测试操作");
        }
        
        Map<String, Object> result = new HashMap<>();
        
        try {
            // 测试MinIO连接
            boolean connected = MinIOConnectionTester.testConnection(
                    minIOManager.getEndpoint(),
                    minIOManager.getAccessKey(),
                    minIOManager.getSecretKey(),
                    minIOManager.getBucketName()
            );
            
            result.put("success", connected);
            result.put("timestamp", new Date());
            
            if (connected) {
                result.put("message", "MinIO连接测试成功");
            } else {
                result.put("message", "MinIO连接测试失败");
            }
            
            return ResultUtils.success(result);
        } catch (Exception e) {
            log.error("MinIO连接测试出错", e);
            result.put("success", false);
            result.put("error", e.getMessage());
            result.put("timestamp", new Date());
            return ResultUtils.success(result);
        }
    }

    /**
     * 测试图片访问服务
     */
    @PostMapping("/check-url")
    public BaseResponse<Map<String, Object>> checkImageUrl(HttpServletRequest request, @RequestPart(required = false) String imageUrl) {
        Map<String, Object> result = new HashMap<>();
        User loginUser = userService.getLoginUser(request);
        
        // 如果没有提供 URL，则使用当前用户的头像
        if (imageUrl == null || imageUrl.isEmpty()) {
            imageUrl = loginUser.getAvatarUrl();
        }
        
        result.put("original_url", imageUrl);
        
        // 检查 URL 格式
        if (imageUrl == null) {
            result.put("storage_type", "无URL");
            result.put("status", "错误");
            result.put("message", "没有提供URL");
            return ResultUtils.success(result);
        }
        
        // 检查 URL 类型
        if (imageUrl.contains(minIOManager.getEndpoint()) || 
                  imageUrl.contains("localhost:9000") || 
                  imageUrl.contains("127.0.0.1:9000") || 
                  imageUrl.contains("localhost:9090") || 
                  imageUrl.contains("127.0.0.1:9090") || 
                  imageUrl.contains("minio")) {
            // 当前是MinIO URL
            result.put("storage_type", "MinIO");
            result.put("minio_endpoint", minIOManager.getEndpoint());
            
            // 尝试从URL中提取存储桶和对象信息
            String path = imageUrl.substring(minIOManager.getEndpoint().length());
            if (path.startsWith("/")) {
                path = path.substring(1);
            }
            
            String[] parts = path.split("/", 2);
            if (parts.length >= 2) {
                String bucketName = parts[0];
                String objectName = parts[1];
                result.put("bucket", bucketName);
                result.put("object", objectName);
                
                // 尝试检查文件是否存在
                try {
                    MinioClient minioClient = MinioClient.builder()
                            .endpoint(minIOManager.getEndpoint())
                            .credentials(minIOManager.getAccessKey(), minIOManager.getSecretKey())
                            .build();
                    
                    StatObjectResponse stat = minioClient.statObject(
                            StatObjectArgs.builder()
                                    .bucket(bucketName)
                                    .object(objectName)
                                    .build()
                    );
                    
                    result.put("exists", true);
                    result.put("size", stat.size());
                    result.put("content_type", stat.contentType());
                    result.put("last_modified", stat.lastModified());
                    result.put("status", "可访问");
                    result.put("message", "MinIO对象存在");
                } catch (Exception e) {
                    // 文件可能不存在或无法访问
                    result.put("exists", false);
                    result.put("error", e.getMessage());
                    result.put("status", "不可访问");
                    result.put("message", "MinIO对象不存在或无法访问");
                }
            } else {
                result.put("status", "无效的MinIO URL格式");
                result.put("message", "无法从URL中解析出存储桶和对象信息");
            }
        } else if (imageUrl.startsWith("http://") || imageUrl.startsWith("https://")) {
            // 其他外部URL
            result.put("storage_type", "外部URL");
            
            // 尝试访问
            try {
                java.net.URL url = new java.net.URL(imageUrl);
                java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
                connection.setConnectTimeout(5000);
                connection.setReadTimeout(5000);
                connection.setRequestMethod("HEAD");
                
                int responseCode = connection.getResponseCode();
                result.put("status_code", responseCode);
                result.put("status", responseCode == 200 ? "可访问" : "不可访问");
                
                if (responseCode == 200) {
                    result.put("content_type", connection.getContentType());
                    result.put("content_length", connection.getContentLength());
                    result.put("message", "外部URL可访问");
                } else {
                    result.put("message", "外部URL无法访问，状态码: " + responseCode);
                }
            } catch (Exception e) {
                result.put("status", "不可访问");
                result.put("error", e.getMessage());
                result.put("message", "外部URL访问失败: " + e.getMessage());
            }
        } else {
            result.put("storage_type", "未知或外部URL");
            result.put("status", "不确定");
            result.put("message", "URL格式未识别，无法确定可访问性");
        }
        
        return ResultUtils.success(result);
    }

    /**
     * 图片代理 - 允许通过后端代理访问 MinIO 中的图片
     * 避免前端直接访问 MinIO 时的跨域问题
     */
    @GetMapping("/proxy")
    public void imageProxy(@RequestParam("url") String imageUrl, HttpServletResponse response) {
        try {
            log.info("收到图片代理请求: {}", imageUrl);
            
            // 安全检查：只允许代理MinIO链接 (通过TCP端口6300穿透)
            boolean isMinioUrl = imageUrl.contains(minIOManager.getEndpoint()) || 
                               imageUrl.contains("localhost:9000") || 
                               imageUrl.contains("127.0.0.1:9000") ||
                               imageUrl.contains("localhost:9090") || 
                               imageUrl.contains("127.0.0.1:9090") ||
                               imageUrl.contains("47.109.146.201:6300") ||
                               imageUrl.contains("minio");
                               
            if (!isMinioUrl) {
                log.warn("拒绝代理非MinIO图片URL: {}", imageUrl);
                response.setStatus(HttpServletResponse.SC_FORBIDDEN);
                return;
            }
            
            log.info("MinIO代理请求 (通过TCP端口6300穿透): {}", imageUrl);
            
            byte[] imageContent;
            String contentType;
            
            // 处理 MinIO URL
            java.net.URL url = new java.net.URL(imageUrl);
            java.net.HttpURLConnection connection = (java.net.HttpURLConnection) url.openConnection();
            connection.setConnectTimeout(5000); // 设置连接超时
            connection.setReadTimeout(10000);   // 设置读取超时
            connection.setRequestMethod("GET");
            
            // 添加一些请求头，避免被服务器拒绝
            connection.setRequestProperty("User-Agent", "Mozilla/5.0");
            connection.setRequestProperty("Accept", "image/webp,image/apng,image/*,*/*;q=0.8");
            
            int responseCode = connection.getResponseCode();
            log.info("MinIO代理请求响应码: {}", responseCode);
            
            if (responseCode != HttpServletResponse.SC_OK) {
                log.warn("MinIO代理请求失败: {}", responseCode);
                response.setStatus(responseCode);
                return;
            }
            
            contentType = connection.getContentType();

            // 读取图片数据
            try (InputStream is = connection.getInputStream()) {
                imageContent = IOUtils.toByteArray(is);
            }
            
            // 设置响应头
            response.setContentType(contentType != null ? contentType : "image/jpeg");
            response.setContentLength(imageContent.length);
            response.setHeader("Cache-Control", "public, max-age=31536000"); // 缓存一年
            
            // 写入响应
            OutputStream os = response.getOutputStream();
            os.write(imageContent);
            os.flush();
            
            log.info("图片代理成功，内容类型: {}, 大小: {} 字节", contentType, imageContent.length);
            
        } catch (Exception e) {
            log.error("图片代理失败: {}", e.getMessage(), e);
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
        }
    }
}